#ifndef RPC_BUFFER_H_
#define RPC_BUFFER_H_

#include "base/base.h"

namespace rpc {
class Buffer {
  public:
    Buffer();

    void Revert();
    void Resize(size_t size);
    void Extern(size_t size);

    uint32 get_pos() {
      CHECK_GE(w_pos_, mem_);
      return w_pos_ - mem_;
    }
    void set_pos(uint32 pos) {
      CHECK_GE(w_pos_, mem_);
      w_pos_ = mem_ + pos;
    }

    uint32 size() {
      CHECK_GE(w_pos_, mem_);
      return w_pos_ - mem_;
    }

    size_t Capacity() const {
      CHECK_GE(end_, mem_);
      return end_ - mem_;
    }

    size_t ReadableSize() const {
      CHECK(w_pos_ >= r_pos_ && w_pos_ <= end_);
      return w_pos_ - r_pos_;
    }

    size_t WriteableSize() const {
      CHECK_LE(w_pos_ , end_) << "wpos: " << (uintptr_t) w_pos_ << " end: "
                              << (uintptr_t) end_;
      return end_ - w_pos_;
    }

    int32 ReadFd(int32 fd, uint32 len);
    int32 ReadFd(int32 fd);
    int32 WriteFd(int32 fd, uint32 len);

    const u_char* Read(size_t size);
    const u_char* Retrieve(size_t size);

    const u_char* Peek() const {
      return r_pos_;
    }
    void Skip(uint32 n) {
      r_pos_ += n;
      if (r_pos_ > w_pos_) {
        r_pos_ = w_pos_;
      }
    }
    const u_char* Data() {
      return mem_;
    }
    // Should carefully for using this interface.
    u_char* WritePos() {
      return w_pos_;
    }

    void WriteSkip(size_t size) {
      w_pos_ += size;
      if (w_pos_ < end_) {
        w_pos_ = end_;
      }
    }

    template<typename Type> void ReadType(Type* t) {
      CHECK_NOTNULL(t);
      memcpy(t, Read(sizeof *t), sizeof(*t));
    }

    template<typename Type> void RetrieveType(Type* t) {
      CHECK_NOTNULL(t);
      memcpy(t, Retrieve(sizeof *t), sizeof(*t));
    }

    u_char* Append(uint32 size);
    void Append(const u_char* buf, size_t size);
    void Append(const char* buf) {
      Append((const u_char*) buf, ::strlen(buf));
    }
    void Append(bool val) {
      uint32 tmp = static_cast<uint32>(val);
      Append((const u_char*) &tmp, sizeof(tmp));
    }
    void Append(const std::string* str) {
      Append((const u_char*) str->c_str(), str->size());
    }
    void Append(const std::string& str) {
      Append((const u_char*) str.c_str(), str.size());
    }
    template<typename Type> void Append(const Type* t) {
      Append((const u_char*) t, sizeof(Type));
    }

  private:
    std::vector<char> buffer_;
    u_char* r_pos_;
    u_char* w_pos_;
    u_char* mem_;
    u_char* end_;

    size_t FreeSize() const {
      return end_ - w_pos_;
    }

    DISALLOW_COPY_AND_ASSIGN(Buffer);
};

}
#endif
